﻿using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Template.Infrastructure.Entity;
using Template.Infrastructure.Enums;
using Template.Infrastructure.Exceptions;
using Template.Infrastructure.Extensions;
using Template.Infrastructure.Ui;

namespace Template.Infrastructure.Entity
{
    public class Repository<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey>
       where TEntity : class, IEntity<TPrimaryKey>
       where TPrimaryKey : IEquatable<TPrimaryKey>
    {
        /// <summary>
        /// 
        /// </summary>
        private readonly DbSet<TEntity> _dbSet = null;
        private readonly DbContext _dbContext = null;
        ///
        private readonly ILogger _logger = null;
        public Repository(IServiceProvider serviceProvider)
        {

            _dbContext = (serviceProvider.GetService(typeof(IUnitOfWork)) as IUnitOfWork).GetDbContext();
            _dbSet = _dbContext.Set<TEntity>();
            _logger = serviceProvider.GetLogger<Repository<TEntity, TPrimaryKey>>();
        }
        #region 查询

        /// <summary>
        /// 获取 <typeparamref name="TEntity"/>不跟踪数据更改（NoTracking）的查询数据源
        /// </summary>
        public IQueryable<TEntity> Entities => _dbSet.AsNoTracking();

        /// <summary>
        /// 获取 <typeparamref name="TEntity"/>跟踪数据更改（Tracking）的查询数据源
        /// </summary>
        public IQueryable<TEntity> TrackEntities => _dbSet;

        /// <summary>
        /// 根据ID得到实体
        /// </summary>
        /// <param name="primaryKey">主键</param>
        /// <returns>返回查询后实体</returns>
        public TEntity GetById(TPrimaryKey primaryKey) => _dbSet.Find(primaryKey);

        /// <summary>
        /// 异步根据ID得到实体
        /// </summary>
        /// <param name="primaryKey">主键</param>
        /// <returns>返回查询后实体</returns>
        public async Task<TEntity> GetByIdAsync(TPrimaryKey primaryKey) => await _dbSet.FindAsync(primaryKey);


        /// <summary>
        /// 根据ID得到Dto实体
        /// </summary>
        /// <param name="primaryKey">主键</param>
        /// <returns>返回查询后实体并转成Dto</returns>
        public TInputDto GetByIdToDto<TInputDto>(TPrimaryKey primaryKey) where TInputDto : class, IInputDto<TPrimaryKey> => this.GetById(primaryKey).MapTo<TInputDto>();

        /// <summary>
        /// 异步根据ID得到Dto实体
        /// </summary>
        /// <param name="primaryKey">主键</param>
        /// <returns></returns>
        public async Task<TInputDto> GetByIdToDtoAsync<TInputDto>(TPrimaryKey primaryKey) where TInputDto : class, IInputDto<TPrimaryKey> => (await this.GetByIdAsync(primaryKey)).MapTo<TInputDto>();

        /// <summary>
        ///查询不跟踪数据源
        /// </summary>
        /// <param name="predicate">条件</param>
        /// <returns>返回查询后数据源</returns>
        public IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> predicate)
        {
            predicate.NotNull(nameof(predicate));
            return this.Entities.Where(predicate);
        }


        /// <summary>
        ///查询跟踪数据源
        /// </summary>
        /// <param name="predicate">条件</param>
        /// <returns>返回查询后数据源</returns>
        public IQueryable<TEntity> TrackQuery(Expression<Func<TEntity, bool>> predicate)
        {
            predicate.NotNull(nameof(predicate));
            return this.TrackEntities.Where(predicate);
        }
        #endregion


        #region 添加
        /// <summary>
        /// 以异步DTO插入实体
        /// </summary>
        /// <typeparam name="TInputDto">添加DTO类型</typeparam>
        /// <param name="dto">添加DTO</param>
        /// <param name="checkFunc">添加信息合法性检查委托</param>
        /// <param name="insertFunc">由DTO到实体的转换委托</param>
        /// <returns>业务操作结果</returns>
        public async Task<OperationResult> InsertAsync<TInputDto>(TInputDto dto, Func<TInputDto, Task> checkFunc = null, Func<TInputDto, TEntity, Task<TEntity>> insertFunc = null, Func<TEntity, TInputDto> completeFunc = null) where TInputDto : IInputDto<TPrimaryKey>
        {
            dto.NotNull(nameof(dto));

            try
            {

                if (checkFunc.IsNotNull())
                {
                    await checkFunc(dto);
                }

                TEntity entity = dto.MapTo<TEntity>();

                if (!insertFunc.IsNull())
                {
                    entity = await insertFunc(dto, entity);
                }
                entity = CheckInsert(entity);
                await _dbSet.AddAsync(entity);

                if (completeFunc.IsNotNull())
                {
                    dto = completeFunc(entity);
                }

                int count = await _dbContext.SaveChangesAsync();
                return new OperationResult(count > 0 ? "添加成功" : "操作没有引发任何变化", count > 0 ? OperationResultType.Success : OperationResultType.NoChanged);
            }
            catch (DomainException e)
            {

                return new OperationResult(e.Message, OperationResultType.Error);
            }
            catch (Exception ex)
            {

                throw ex;
            }


        }

        /// <summary>
        /// 以异步插入实体
        /// </summary>
        /// <param name="entity">要插入实体</param>
        /// <returns></returns>
        public async Task<int> InsertAsync(TEntity entity)
        {
            entity.NotNull(nameof(entity));
            entity = CheckInsert(entity);

            await _dbSet.AddAsync(entity);
            return await _dbContext.SaveChangesAsync();
        }


        /// <summary>
        /// 以异步批量插入实体
        /// </summary>
        /// <param name="entitys">要插入实体集合</param>
        /// <returns></returns>
        public async Task<int> InsertRangeAsync(TEntity[] entitys)
        {
            entitys.NotNull(nameof(entitys));
            entitys = CheckInsert(entitys);

            await _dbSet.AddRangeAsync(entitys);
            return await _dbContext.SaveChangesAsync();
        }

        #endregion

        #region 更新


        /// <summary>
        /// 以异步DTO更新实体
        /// </summary>
        /// <typeparam name="TInputDto">更新DTO类型</typeparam>
        /// <param name="dto">更新DTO</param>
        /// <param name="checkFunc">添加信息合法性检查委托</param>
        /// <param name="updateFunc">由DTO到实体的转换委托</param>
        /// <returns>业务操作结果</returns>
        public async Task<OperationResult> UpdateAsync<TInputDto>(TInputDto dto, Func<TInputDto, TEntity, Task> checkFunc = null, Func<TInputDto, TEntity, Task<TEntity>> updateFunc = null) where TInputDto : class, IInputDto<TPrimaryKey>, new()
        {
            dto.NotNull(nameof(dto));
            try
            {
                TEntity entity = await this.GetByIdAsync(dto.Id);

                if (entity.IsNull())
                {
                    return new OperationResult($"该{dto.Id}键的数据不存在", OperationResultType.QueryNull);
                }

                if (checkFunc.IsNotNull())
                {
                    await checkFunc(dto, entity);
                }


                entity = dto.MapTo(entity);
                if (!updateFunc.IsNull())
                {
                    entity = await updateFunc(dto, entity);
                }
                entity = CheckUpdate(entity);
                await Task.Run(() => _dbSet.Update(entity));
                int count = await _dbContext.SaveChangesAsync();
                return new OperationResult(count > 0 ? "更新成功" : "操作没有引发任何变化", count > 0 ? OperationResultType.Success : OperationResultType.NoChanged);
            }
            catch (DomainException e)
            {

                return new OperationResult(e.Message, OperationResultType.Error);
            }
            catch (Exception ex)
            {

                throw ex;
            }

        }
        #endregion



        #region 删除

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="primaryKey"></param>
        /// <returns></returns>
        public async Task<OperationResult> DeleteAsync(TPrimaryKey primaryKey)
        {
            TEntity entity = await this.GetByIdAsync(primaryKey);

            if (entity.IsNull())
            {
                return new OperationResult($"该{primaryKey}键的数据不存在", OperationResultType.QueryNull);
            }

            if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
            {
                ISoftDelete softDeletableEntity = (ISoftDelete)entity;
                softDeletableEntity.IsDeleted = true;
                entity = (TEntity)softDeletableEntity;
                await Task.Run(() => this._dbContext.Update(entity));
            }
            else
            {
                await Task.Run(() => this._dbContext.Remove(entity));
            }


            int count = await _dbContext.SaveChangesAsync();
            return new OperationResult(count > 0 ? "删除成功" : "操作没有引发任何变化", count > 0 ? OperationResultType.Success : OperationResultType.NoChanged);
        }
        #endregion


        #region 其他

        /// <summary>
        /// 检查创建
        /// </summary>
        /// <param name="entitys">实体集合</param>
        /// <returns></returns>

        private TEntity[] CheckInsert(TEntity[] entitys)
        {

            for (int i = 0; i < entitys.Length; i++)
            {
                var entity = entitys[i];
                entitys[i] = CheckInsert(entity);
            }
            return entitys;
        }


        /// <summary>
        /// 检查创建时间
        /// </summary>
        /// <param name="entity">实体</param>
        /// <returns></returns>
        private TEntity CheckInsert(TEntity entity)
        {

            var creationAudited = entity.GetType().GetInterface(/*$"ICreationAudited`1"*/typeof(ICreationAudited<>).Name);
            if (creationAudited == null)
            {
                return entity;
            }

            var typeArguments = creationAudited?.GenericTypeArguments[0];
            var fullName = typeArguments?.FullName;
            if (fullName == typeof(int).FullName)
            {
                entity = CheckICreationAudited<int>(entity);
            }
            else if (fullName == typeof(Guid).FullName)
            {
                entity = CheckICreationAudited<Guid>(entity);

            }
            else if (fullName == typeof(long).FullName)
            {
                entity = CheckICreationAudited<long>(entity);
            }

            return entity;

        }

        private TEntity CheckICreationAudited<TUserKey>(TEntity entity)
           where TUserKey : struct, IEquatable<TUserKey>
        {
            if (!entity.GetType().IsBaseOn(typeof(ICreationAudited<>)))
            {
                return entity;
            }

            ICreationAudited<TUserKey> entity1 = (ICreationAudited<TUserKey>)entity;

            entity1.CreatedTime = DateTime.Now;
            return (TEntity)entity1;
        }

        /// <summary>
        /// 检查最后修改时间
        /// </summary>
        /// <param name="entitys"></param>
        /// <returns></returns>
        private TEntity[] CheckUpdate(TEntity[] entitys)
        {

            for (int i = 0; i < entitys.Length; i++)
            {
                var entity = entitys[i];
                entitys[i] = CheckUpdate(entity);
            }
            return entitys;
        }

        /// <summary>
        /// 检查最后修改时间
        /// </summary>
        /// <param name="entity">实体</param>
        /// <returns></returns>
        private TEntity CheckUpdate(TEntity entity)
        {

            var creationAudited = entity.GetType().GetInterface(/*$"ICreationAudited`1"*/typeof(IModificationAudited<>).Name);
            if (creationAudited == null)
            {
                return entity;
            }

            var typeArguments = creationAudited?.GenericTypeArguments[0];
            var fullName = typeArguments?.FullName;
            if (fullName == typeof(int).FullName)
            {
                entity = CheckIModificationAudited<int>(entity);
            }
            else if (fullName == typeof(Guid).FullName)
            {
                entity = CheckIModificationAudited<Guid>(entity);

            }
            else if (fullName == typeof(long).FullName)
            {
                entity = CheckIModificationAudited<long>(entity);
            }

            return entity;

        }
        /// <summary>
        /// 检查最后修改时间
        /// </summary>
        /// <typeparam name="TUserKey"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        public TEntity CheckIModificationAudited<TUserKey>(TEntity entity)
      where TUserKey : struct, IEquatable<TUserKey>
        {
            if (!entity.GetType().IsBaseOn(typeof(IModificationAudited<>)))
            {
                return entity;
            }

            IModificationAudited<TUserKey> entity1 = (IModificationAudited<TUserKey>)entity;

            entity1.LastModifierTime = DateTime.Now;
            return (TEntity)entity1;
        }
        #endregion

    }


}
